#include "class.hpp"

demo_class::person::person(uint32_t age, std::string name) : age_(age), name_(name)
{
    LOGINFO("create person :%{public}s %{public}d", name_.c_str(), age_);
}

napi_value demo_class::person::SayAge(napi_env env, napi_callback_info info)
{
    napi_value res;
    napi_status status;
    napi_value jsthis;
    status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
    person *obj;
    status = napi_unwrap(env, jsthis, (void**)&obj);
    STATUS_CHECK(status);
    
    status = napi_create_int32(env, obj->age_, &res);
    STATUS_CHECK(status);
    
    return res;
}

napi_value demo_class::person::SayName(napi_env env, napi_callback_info info) {
    napi_value res;
    napi_status status;
    napi_value jsthis;
    status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
    person *obj;
    status = napi_unwrap(env, jsthis, (void **)&obj);
    STATUS_CHECK(status);

    status = napi_create_string_utf8(env, obj->name_.c_str(), NAPI_AUTO_LENGTH, &res);
    STATUS_CHECK(status);

    return res;
}

napi_value demo_class::person::SetName(napi_env env, napi_callback_info info)
{
    napi_value res = nullptr;
    napi_status status;
    napi_value jsthis;
    napi_value argv[1];
    size_t argc = 1;
    
    status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
    STATUS_CHECK(status);
    
    person *obj;
    status = napi_unwrap(env, jsthis, (void**)&obj);
    STATUS_CHECK(status);
    
    char buf[BUFF_SIZE] = {0};
    size_t len = 0;
    status = napi_get_value_string_utf8(env, argv[0], buf, BUFF_SIZE, &len);
    STATUS_CHECK(status);

    obj->name_ = std::string(buf);
    
    return res;
}

napi_value demo_class::person::GetName(napi_env env, napi_callback_info info) {
    napi_value res = nullptr;
    napi_status status;
    napi_value jsthis;
    napi_value argv[1];
    size_t argc = 1;

    status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
    STATUS_CHECK(status);

    person *obj;
    status = napi_unwrap(env, jsthis, (void **)&obj);
    STATUS_CHECK(status);

    status = napi_create_string_utf8(env, obj->name_.c_str(), obj->name_.size(), &res);
    STATUS_CHECK(status);

    return res;
}

napi_value demo_class::person::Construct(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value argv[2] = {nullptr};
    napi_value jsthis = nullptr;
    
    napi_status status;
    status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
    STATUS_CHECK(status);
    
    int32_t age;
    char buf[BUFF_SIZE] = {0};
    size_t len = 0;
    
    status = napi_get_value_int32(env, argv[0], &age);
    STATUS_CHECK(status);
    
    status = napi_get_value_string_utf8(env, argv[1], buf, BUFF_SIZE, &len);
    STATUS_CHECK(status);
    
    person *ps = new person(age, std::string(buf));
    
    status = napi_wrap(env, jsthis, ps, [](napi_env env, void* finalize_data, void* finalize_hint)
                                        {
                                            delete reinterpret_cast<demo_class::person *>(finalize_data); 
                                        }, nullptr, nullptr);
    STATUS_CHECK(status);
    
//     uint32_t length = 0;
//     status = napi_reference_unref(env, ps->ref, &length); // 这里添加这个函数的原因：对标原生nodeJS测试结果， 原生接口linux上测试返回的ref的计数应该是0， 而ohos的返回是1，导致计数没有清零，GC无法回收，造成内存泄漏
//     STATUS_CHECK(status);
    return jsthis;
}

napi_value demo_class::person::Exports(napi_env env, napi_value &exports)
{
    napi_property_descriptor descClass[] = {
        {"SayAge", nullptr, demo_class::person::SayAge, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"SayName", nullptr, demo_class::person::SayName, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"name", nullptr, nullptr, demo_class::person::GetName, demo_class::person::SetName, nullptr, napi_default, nullptr}
    };
    
    napi_status status;
    napi_value cons;
    napi_ref *ref = new napi_ref;
    status = napi_define_class(env, "person", NAPI_AUTO_LENGTH, demo_class::person::Construct, nullptr, sizeof(descClass) / sizeof(descClass[0]), descClass, &cons);
    STATUS_CHECK(status);
    
    status = napi_create_reference(env, cons, 1, ref);
    STATUS_CHECK(status);
    
    status = napi_set_named_property(env, exports, "person", cons);
    STATUS_CHECK(status);

    napi_property_descriptor descFunc[] = {
        {"GetPerson", nullptr, demo_class::GetPerson, nullptr, nullptr, nullptr, napi_default, nullptr}
    };

    napi_define_properties(env, exports, sizeof(descFunc) / sizeof(descFunc[0]), descFunc);

    std::map<std::string, napi_ref *> *refArray;
    status = napi_get_instance_data(env, (void **)&refArray);
    if (refArray != nullptr) {
        (*refArray)["person"] = ref;
    }
    
    return exports;
}


//返回类对象
napi_value demo_class::GetPerson(napi_env env, napi_callback_info info) {
    napi_value constructs;
    napi_status status;

    std::map<std::string, napi_ref *> *refMap;
    status = napi_get_instance_data(env, (void **)&refMap);

    status = napi_get_reference_value(env, *(refMap->at("person")), &constructs);
    STATUS_CHECK(status);

    size_t argc = 2;
    napi_value argv[2];

    status = napi_create_int32(env, 18, &argv[0]);
    STATUS_CHECK(status);

    status = napi_create_string_utf8(env, "xiaoli", NAPI_AUTO_LENGTH, &argv[1]);
    STATUS_CHECK(status);

    napi_value instance;
    status = napi_new_instance(env, constructs, argc, argv, &instance);
    STATUS_CHECK(status);
    
    person *ps;
    status = napi_unwrap(env, instance, (void**)&ps);
    ps->name_ = "xiaoxiao";

    return instance;
}